#include "SQLCreateTableStatement.h"

#include "../expr/SQLIdentifierExpr.h"

#include "../../SQLUtils.h"
#include "../../../utils/instanceof.h"
#include "../../../utils/ListDG.h"
#include "../../../utils/FnvHash.h"
#include "../../../utils/StringUtils.h"
#include "../../parser/SQLParserUtils.h"
#include "../../../Exception/IllegalArgumentException.h"

#include "SQLColumnDefinition.h"
#include "SQLTableElement.h"
#include "SQLColumnConstraint.h"
#include "../../visitor/SQLASTVisitor.h"
#include "../SQLPartitionBy.h"
#include "../../../utils/listTran.h"
#include "../../../utils/log.h"
#include "../../../sql/ast/statement/SQLAlterTableItem.h"
#include "../expr/SQLValuableExpr.h"
#include "SQLExternalRecordFormat.h"
#include "SQLDropIndexStatement.h"
#include "../../dialect/mysql/ast/statement/MySqlTableIndex.h"

SQLCreateTableStatement::SQLCreateTableStatement()
{
  tableElementList = std::make_shared<std::list<SQLTableElement_ptr>>();
  partitionColumns = std::make_shared<std::list<SQLColumnDefinition_ptr>>();
  clusteredBy = std::make_shared<std::list<SQLSelectOrderByItem_ptr>>();
  sortedBy = std::make_shared<std::list<SQLSelectOrderByItem_ptr>>();
  tableOptions = std::make_shared<std::list<SQLAssignItem_ptr>>();
  tblProperties = std::make_shared<std::list<SQLAssignItem_ptr>>();
}

SQLCreateTableStatement::SQLCreateTableStatement(DbType_ptr dbType)
    : SQLStatementImpl(dbType)
{
  tableElementList = std::make_shared<std::list<SQLTableElement_ptr>>();
  partitionColumns = std::make_shared<std::list<SQLColumnDefinition_ptr>>();
  clusteredBy = std::make_shared<std::list<SQLSelectOrderByItem_ptr>>();
  sortedBy = std::make_shared<std::list<SQLSelectOrderByItem_ptr>>();
  tableOptions = std::make_shared<std::list<SQLAssignItem_ptr>>();
  tblProperties = std::make_shared<std::list<SQLAssignItem_ptr>>();
}

void SQLCreateTableStatement::accept0(SQLASTVisitor_ptr v)
{
  if (v->visit(SharedObject(SQLCreateTableStatement)))
  {
    acceptChild(v);
  }
  v->endVisit(SharedObject(SQLCreateTableStatement));
}

void SQLCreateTableStatement::acceptChild(SQLASTVisitor_ptr v)
{
  SQLObjectImpl::acceptChild(v, tableSource);
  SQLObject_list_ptr tmp = ListTran::tran_class<SQLTableElement, SQLObject>(this->tableElementList);
  SQLObjectImpl::acceptChild(v, tmp);
  SQLObjectImpl::acceptChild(v, inherits);
  SQLObjectImpl::acceptChild(v, select);
  SQLObjectImpl::acceptChild(v, comment);
  SQLObjectImpl::acceptChild(v, like);

  SQLObjectImpl::acceptChild(v, tablespace);
  SQLObjectImpl::acceptChild(v, partitioning);
  SQLObjectImpl::acceptChild(v, localPartitioning);
  SQLObjectImpl::acceptChild(v, storedAs);
  SQLObjectImpl::acceptChild(v, location);

  SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLColumnDefinition, SQLObject>(this->partitionColumns);
  SQLObjectImpl::acceptChild(v, tmp1);
  SQLObject_list_ptr tmp2 = ListTran::tran_class<SQLSelectOrderByItem, SQLObject>(this->clusteredBy);
  SQLObjectImpl::acceptChild(v, tmp2);
  SQLObject_list_ptr tmp3 = ListTran::tran_class<SQLSelectOrderByItem, SQLObject>(this->sortedBy);
  SQLObjectImpl::acceptChild(v, tmp3);
  SQLObject_list_ptr tmp4 = ListTran::tran_class<SQLAssignItem, SQLObject>(this->tableOptions);
  SQLObjectImpl::acceptChild(v, tmp4);
  SQLObject_list_ptr tmp5 = ListTran::tran_class<SQLAssignItem, SQLObject>(this->tblProperties);
  SQLObjectImpl::acceptChild(v, tmp5);
}

SQLExpr_ptr SQLCreateTableStatement::getComment()
{
  return comment;
}

void SQLCreateTableStatement::setComment(SQLExpr_ptr comment)
{
  if (comment != NULL)
  {
    comment->setParent(SharedObject(SQLCreateTableStatement));
  }
  this->comment = comment;
}

SQLName_ptr SQLCreateTableStatement::getName()
{
  if (tableSource == NULL)
  {
    return NULL;
  }

  return std::dynamic_pointer_cast<SQLName>(tableSource->getExpr());
}

string_ptr SQLCreateTableStatement::getTableName()
{
  SQLName_ptr name = getName();
  if (name == NULL)
  {
    return NULL;
  }
  return name->getSimpleName();
}

string_ptr SQLCreateTableStatement::getSchema()
{
  SQLName_ptr name = getName();
  if (name == NULL)
  {
    return NULL;
  }

  // if (name instanceof SQLPropertyExpr) {
  //     return ((SQLPropertyExpr*) name)->getOwnernName();
  // }

  return NULL;
}

void SQLCreateTableStatement::setSchema(string_ptr name)
{
  if (this->tableSource == NULL)
  {
    return;
  }
  tableSource->setSchema(name);
}

void SQLCreateTableStatement::setName(SQLName_ptr name)
{
  this->setTableSource(SQLExprTableSource_ptr(new SQLExprTableSource(std::dynamic_pointer_cast<SQLExpr>(name))));
}

void SQLCreateTableStatement::setName(string_ptr name)
{
  this->setName(SQLIdentifierExpr_ptr(new SQLIdentifierExpr(name)));
}

SQLExprTableSource_ptr SQLCreateTableStatement::getTableSource()
{
  return tableSource;
}

void SQLCreateTableStatement::setTableSource(SQLExprTableSource_ptr tableSource)
{
  if (tableSource != NULL)
  {
    tableSource->setParent(SharedObject(SQLCreateTableStatement));
  }
  this->tableSource = tableSource;
}

void SQLCreateTableStatement::setTableName(string_ptr tableName)
{
  SQLExpr_ptr name = SQLUtils::toSQLExpr(tableName, dbType);
  setTableSource(SQLExprTableSource_ptr(new SQLExprTableSource(name)));
}

SQLCreateTableStatement::Type SQLCreateTableStatement::getType()
{
  return type;
}

void SQLCreateTableStatement::setType(SQLCreateTableStatement::Type type)
{
  this->type = type;
}

SQLTableElement_list_ptr SQLCreateTableStatement::getTableElementList()
{
  return tableElementList;
}

SQLColumnDefinition_ptr SQLCreateTableStatement::getColumn(string_ptr columnName)
{
  long hashCode64 = FnvHash::hashCode64(columnName);

  for (SQLTableElement_ptr e : *tableElementList)
  {
    if (instanceof <SQLTableElement, SQLColumnDefinition>(e))
    {
      SQLColumnDefinition_ptr column = std::dynamic_pointer_cast<SQLColumnDefinition>((e));
      if (column->nameHashCode64() == hashCode64)
      {
        return column;
      }
    }
  }

  return NULL;
}

SQLColumnDefinition_list_ptr SQLCreateTableStatement::getColumnDefinitions()
{
  SQLColumnDefinition_list_ptr column = std::make_shared<std::list<SQLColumnDefinition_ptr>>();
  for (SQLTableElement_ptr element : *this->tableElementList)
  {
    if (instanceof <SQLTableElement, SQLColumnDefinition>(element))
    {
      column->push_back(std::dynamic_pointer_cast<SQLColumnDefinition>(element));
    }
  }
  return column;
}

string_list_ptr SQLCreateTableStatement::getColumnNames(BOOL_ptr normalized)
{
  string_list_ptr columnNames;
  for (SQLColumnDefinition_ptr definition : *getColumnDefinitions())
  {
    string_ptr columnName = (definition->getColumnName());
    if (normalized)
    {
      columnName = SQLUtils::normalize(columnName);
    }
    columnNames->push_back(columnName);
  }

  return columnNames;
}

string_list_ptr SQLCreateTableStatement::getColumnComments()
{
  string_list_ptr comments = std::make_shared<std::list<string_ptr>>();
  for (SQLColumnDefinition_ptr definition : *getColumnDefinitions())
  {
    comments->push_back((std::dynamic_pointer_cast<SQLCharExpr>(definition->getComment()))->getText());
  }
  return comments;
}

string_list_ptr SQLCreateTableStatement::getPrimaryKeyNames()
{
  string_list_ptr keys = std::make_shared<std::list<string_ptr>>();
  for (SQLTableElement_ptr element : *this->tableElementList)
  {
    // if (instanceof <SQLTableElement, MySqlPrimaryKey>(element)) {
    //     std::list<SQLSelectOrderByItem*> columns = (dynamic_cast<MySqlPrimaryKey *>(element))->getColumns();
    //     for (SQLSelectOrderByItem* column : columns) {
    //         keys.push_back(SQLUtils::normalize(column->getExpr()->toString()));
    //     }
    // }
  }
  return keys;
}

void SQLCreateTableStatement::addColumn(string_ptr columnName, string_ptr dataType)
{
  SQLColumnDefinition_ptr column = SQLColumnDefinition_ptr(new SQLColumnDefinition());
  column->setName(columnName);
  column->setDataType(SQLParserUtils::createExprParser(dataType, dbType)->parseDataType());
  addColumn(column);
}

void SQLCreateTableStatement::addColumn(SQLColumnDefinition_ptr column)
{
  if (column == NULL)
  {
    throw new IllegalArgumentException();
  }
  column->setParent(SharedObject(SQLCreateTableStatement));
  tableElementList->push_back(column);
}

BOOL_ptr SQLCreateTableStatement::isIfNotExists()
{
  return ifNotExists;
}

void SQLCreateTableStatement::setIfNotExiists(BOOL_ptr ifNotExists)
{
  this->ifNotExists = ifNotExists;
}

SQLExprTableSource_ptr SQLCreateTableStatement::getInherits()
{
  return inherits;
}

void SQLCreateTableStatement::setInherits(SQLExprTableSource_ptr inherits)
{
  if (inherits != NULL)
  {
    inherits->setParent(SharedObject(SQLCreateTableStatement));
  }
  this->inherits = inherits;
}

SQLSelect_ptr SQLCreateTableStatement::getSelect()
{
  return select;
}

void SQLCreateTableStatement::setSelect(SQLSelect_ptr select)
{
  if (select != NULL)
  {
    select->setParent(SharedObject(SQLCreateTableStatement));
  }
  this->select = select;
}

SQLExprTableSource_ptr SQLCreateTableStatement::getLike()
{
  return like;
}

void SQLCreateTableStatement::setLike(SQLName_ptr like)
{
  this->setLike(SQLExprTableSource_ptr(new SQLExprTableSource(std::dynamic_pointer_cast<SQLExpr>(like))));
}

void SQLCreateTableStatement::setLike(SQLExprTableSource_ptr like)
{
  if (like != NULL)
  {
    like->setParent(SharedObject(SQLCreateTableStatement));
  }
  this->like = like;
}

BOOL_ptr SQLCreateTableStatement::getCompress()
{
  return compress;
}

void SQLCreateTableStatement::setCompress(BOOL_ptr compress)
{
  this->compress = compress;
}

BOOL_ptr SQLCreateTableStatement::getLogging()
{
  return logging;
}

void SQLCreateTableStatement::setLogging(BOOL_ptr logging)
{
  this->logging = logging;
}

SQLName_ptr SQLCreateTableStatement::getTablespace()
{
  return tablespace;
}

void SQLCreateTableStatement::setTablespace(SQLName_ptr x)
{
  if (x != NULL)
  {
    x->setParent(SharedObject(SQLCreateTableStatement));
  }
  this->tablespace = x;
}

SQLPartitionBy_ptr SQLCreateTableStatement::getPartitioning()
{
  return partitioning;
}

SQLPartitionBy_ptr SQLCreateTableStatement::getLocalPartitioning()
{
  return this->localPartitioning;
}

void SQLCreateTableStatement::setPartitioning(SQLPartitionBy_ptr partitioning)
{
  if (partitioning != NULL)
  {
    partitioning->setParent(SharedObject(SQLCreateTableStatement));
  }

  this->partitioning = partitioning;
}

void SQLCreateTableStatement::setLocalPartitioning(SQLPartitionBy_ptr localPartitioning)
{
  if (localPartitioning != NULL)
  {
    localPartitioning->setParent(SharedObject(SQLCreateTableStatement));
  }

  this->localPartitioning = localPartitioning;
}

SQLObject_list_ptr SQLCreateTableStatement::getChildren()
{
  SQLObject_list_ptr children = std::make_shared<std::list<SQLObject_ptr>>();
  children->push_back(std::dynamic_pointer_cast<SQLObject>(tableSource));
  // children->addAll(tableElementList);
  for (auto ele : *tableElementList)
  {
    children->push_back(std::dynamic_pointer_cast<SQLObject>(ele));
  }
  if (inherits != NULL)
  {
    children->push_back(std::dynamic_pointer_cast<SQLObject>(inherits));
  }
  if (select != NULL)
  {
    children->push_back(std::dynamic_pointer_cast<SQLObject>(select));
  }
  return children;
}

// @SuppressWarnings("unchecked")
void SQLCreateTableStatement::addBodyBeforeComment(string_list_ptr comments)
{
  // if (attributes_ == NULL) {
  //     attributes_ = new HashMap<string_ptr , Object_ptr >(1);
  // }
  auto it = attributes_->find(make_string_ptr("rowFormat->body_before_comment"));
  string_list_ptr attrComments = std::make_shared<std::list<string_ptr>>();
  if (it != attributes_->end())
  {
    attrComments = it->second;
  }
  if (attrComments->empty())
  {
    attributes_->insert(std::make_pair(make_string_ptr("rowFormat->body_before_comment"), comments));
  }
  else
  {
    // attrComments->addAll(comments);
    for (auto ele : *comments)
    {
      attrComments->push_back(ele);
    }
  }
}

// @SuppressWarnings("unchecked")
string_list_ptr SQLCreateTableStatement::getBodyBeforeCommentsDirect()
{
  // if (attributes_ == NULL) {
  //     return NULL;
  // }
  // auto it = attributes_.find("rowFormat->body_before_comment");
  // if (it != attributes_.end())
  // {
  //   return it->second;
  // }

  string_list_ptr attrComments = std::make_shared<std::list<string_ptr>>();
  return attrComments;
}

BOOL_ptr SQLCreateTableStatement::hasBodyBeforeComment()
{
  string_list_ptr comments = getBodyBeforeCommentsDirect();
  if (comments->empty())
  {
    return BOOL::FALSE;
  }

  if ( !comments->empty()){
    return BOOL::TRUE;
  } else {
    return BOOL::FALSE;
  }
}

string_ptr SQLCreateTableStatement::computeName()
{
  if (tableSource == NULL)
  {
    return NULL;
  }

  SQLExpr_ptr expr = tableSource->getExpr();
  if (instanceof <SQLExpr, SQLName>(expr))
  {
    string_ptr name = (std::dynamic_pointer_cast<SQLName>(expr))->getSimpleName();
    return SQLUtils::normalize(name);
  }

  return NULL;
}

SQLColumnDefinition_ptr SQLCreateTableStatement::findColumn(string_ptr columName)
{
  if (columName->empty())
  {
    return NULL;
  }

  long hash = FnvHash::hashCode64(columName);
  return findColumn(hash);
}

SQLColumnDefinition_ptr SQLCreateTableStatement::findColumn(long columName_hash)
{
  for (SQLTableElement_ptr element : *tableElementList)
  {
    if (instanceof <SQLTableElement, SQLColumnDefinition>(element))
    {
      SQLColumnDefinition_ptr column = std::dynamic_pointer_cast<SQLColumnDefinition>(element);
      if (column->nameHashCode64() == columName_hash)
      {
        return column;
      }
    }
  }

  for (SQLColumnDefinition_ptr column : *partitionColumns)
  {
    if (column->nameHashCode64() == columName_hash)
    {
      return column;
    }
  }

  return NULL;
}

BOOL_ptr SQLCreateTableStatement::isPrimaryColumn(string_ptr columnName)
{
  SQLPrimaryKey_ptr pk = this->findPrimaryKey();

  if (pk != NULL && pk->containsColumn(columnName))
  {
    return BOOL::TRUE;
  }

  for (SQLColumnDefinition_ptr element : *this->getColumnDefinitions())
  {
    for (std::shared_ptr<SQLColumnConstraint> constraint : *element->constraints)
    {
      if (instanceof <SQLColumnConstraint, SQLColumnPrimaryKey>(constraint) &&
                         StringUtils::equalsIgnoreCase(SQLUtils::normalize(element->getColumnName()), SQLUtils::normalize(columnName)))
      {
        return BOOL::TRUE;
      }
    }
  }

  return BOOL::FALSE;
}

BOOL_ptr SQLCreateTableStatement::isPrimaryColumn(long columnNameHash)
{
  SQLPrimaryKey_ptr pk = this->findPrimaryKey();
  if (pk == NULL)
  {
    return BOOL::FALSE;
  }

  return pk->containsColumn(columnNameHash);
}

BOOL_ptr SQLCreateTableStatement::isOnlyPrimaryKey(long columnNameHash)
{
  SQLPrimaryKey_ptr pk = this->findPrimaryKey();
  if (pk == NULL)
  {
    return BOOL::FALSE;
  }

  if( pk->containsColumn(columnNameHash) && pk->getColumns()->size() == 1){
    BOOL::TRUE;
  }else{
    BOOL::FALSE;
  }
}

/**
 * only for show columns
 */
BOOL_ptr SQLCreateTableStatement::isMUL(string_ptr columnName)
{
  for (SQLTableElement_ptr element : *this->tableElementList)
  {
    // if (instanceof<SQLTableElement, MySqlUnique>(element))
    // {
    //   MySqlUnique *unique = dynamic_cast<MySqlUnique*>(element);

    //   SQLExpr_ptr column = unique->getColumns()->get(0)->getExpr();
    //   if (instanceof<SQLExpr, SQLIdentifierExpr>(column) && SQLUtils::nameEquals(columnName, (dynamic_cast<SQLIdentifierExpr*>(column))->getName()))
    //   {
    //     return unique->getColumns().size() > 1;
    //   }
    //   else if (instanceof<SQLExpr, SQLMethodInvokeExpr>(column) && SQLUtils::nameEquals((dynamic_cast<SQLMethodInvokeExpr*>(column))->getMethodName(), columnName))
    //   {
    //     return BOOL::TRUE;
    //   }
    // }
    // else if (instanceof<SQLTableElement, MySqlKey>(element))
    // {
    //   MySqlKey* unique = dynamic_cast<MySqlKey*>(element);

    //   std::list<SQLName_ptr >::iterator it = unique->getColumns().begin();
    //   std::advance(it, 0);
    //   SQLExpr_ptr column = (*it)->getExpr();
    //   if (instanceof<SQLExpr,SQLIdentifierExpr>(column)   && SQLUtils::nameEquals(columnName, (dynamic_cast<SQLIdentifierExpr*>(column))->getName()))
    //   {
    //     return BOOL::TRUE;
    //   }
    //   else if (instanceof<SQLExpr,SQLMethodInvokeExpr>(column) && SQLUtils::nameEquals((dynamic_cast<SQLMethodInvokeExpr*>(column))->getMethodName(), columnName))
    //   {
    //     return BOOL::TRUE;
    //   }
    // }
  }
  return BOOL::FALSE;
}

/**
 * only for show columns
 */
BOOL_ptr SQLCreateTableStatement::isUNI(string_ptr columnName)
{
  for (SQLTableElement_ptr element : *this->tableElementList)
  {
    // if (instanceof<SQLTableElement, MySqlUnique>(element) )
    // {
    //   MySqlUnique *unique = dynamic_cast<MySqlUnique*>(element);

    //   if (unique->getColumns()->isEmpty())
    //   {
    //     continue;
    //   }
    //   std::list<SQLName_ptr >::iterator it = unique->getColumns().begin();
    //   std::advance(it, 0);
    //   SQLExpr_ptr column = (*it)->getExpr();
    //   if (instanceof<SQLExpr, SQLIdentifierExpr>(column) && SQLUtils::nameEquals(columnName, (dynamic_cast<SQLIdentifierExpr*>(column))->getName()))
    //   {
    //     return unique->getColumns()->size() == 1;
    //   }
    //   else if (instanceof<SQLExpr, SQLMethodInvokeExpr>(column) && SQLUtils::nameEquals((dynamic_cast<SQLMethodInvokeExpr*>(column))->getMethodName(), columnName))
    //   {
    //     return BOOL::TRUE;
    //   }
    // }
  }
  return BOOL::FALSE;
}

// MySqlUnique *SQLCreateTableStatement::findUnique(string_ptr  columnName)
// {
//   for (SQLTableElement_ptr element : this->tableElementList)
//   {
//     if (instanceof<SQLTableElement,MySqlUnique>(element))
//     {
//       MySqlUnique *unique = dynamic_cast<MySqlUnique*>(element);

//       if (unique->containsColumn(columnName))
//       {
//         return unique;
//       }
//     }
//   }

//   return NULL;
// }

SQLTableElement_ptr SQLCreateTableStatement::findIndex(string_ptr columnName)
{
  for (SQLTableElement_ptr element : *tableElementList)
  {
    if (instanceof <SQLTableElement, SQLUniqueConstraint>(element))
    {
      std::shared_ptr<SQLUniqueConstraint> unique = std::dynamic_pointer_cast<SQLUniqueConstraint>(element);
      for (SQLSelectOrderByItem_ptr item : *unique->getColumns())
      {
        SQLExpr_ptr columnExpr = item->getExpr();
        if (instanceof <SQLExpr, SQLIdentifierExpr>(columnExpr))
        {
          string_ptr keyColumName = (std::dynamic_pointer_cast<SQLIdentifierExpr>(columnExpr))->getName();
          keyColumName = SQLUtils::normalize(keyColumName);
          if (StringUtils::equalsIgnoreCase(keyColumName, columnName))
          {
            return element;
          }
        }
      }
      // }
      // else if (instanceof<SQLTableElement, MySqlTableIndex>(element))
      // {
      //   std::list<SQLSelectOrderByItem *> indexColumns = (dynamic_cast<MySqlTableIndex*>(element))->getColumns();
      //   for (SQLSelectOrderByItem *orderByItem : indexColumns)
      //   {
      //     SQLExpr_ptr columnExpr = orderByItem->getExpr();
      //     if (instanceof<SQLExpr, SQLIdentifierExpr>(columnExpr)  )
      //     {
      //       string_ptr  keyColumName = (dynamic_cast<SQLIdentifierExpr*>(columnExpr))->getName();
      //       keyColumName = SQLUtils::normalize(keyColumName);
      //       if (StringUtils::equalsIgnoreCase(keyColumName, columnName))
      //       {
      //         return element;
      //       }
      //     }
      //   }
    }
  }

  return NULL;
}

// void SQLCreateTableStatement::forEachColumn(Consumer<SQLColumnDefinition *> columnConsumer)
// {
//   if (columnConsumer == NULL)
//   {
//     return;
//   }

//   for (SQLTableElement_ptr element : this->tableElementList)
//   {
//     if (instanceof<SQLTableElement, SQLColumnDefinition>(element))
//     {
//       columnConsumer->accept(dynamic_cast<SQLColumnDefinition*>(element));
//     }
//   }
// }

SQLPrimaryKey_ptr SQLCreateTableStatement::findPrimaryKey()
{
  for (SQLTableElement_ptr element : *this->tableElementList)
  {
    if (instanceof <SQLTableElement, SQLPrimaryKey>(element))
    {
      return std::dynamic_pointer_cast<SQLPrimaryKey>(element);
    }
  }

  return NULL;
}

// std::list<SQLForeignKeyConstraint*> SQLCreateTableStatement::findForeignKey()
// {
//   std::list<SQLForeignKeyConstraint*> fkList ;
//   for (SQLTableElement_ptr element : this->tableElementList)
//   {
//     if (instanceof<SQLTableElement, SQLForeignKeyConstraint>(element)  )
//     {
//       fkList.push_back(dynamic_cast<SQLForeignKeyConstraint*>(element));
//     }
//   }
//   return fkList;
// }

BOOL_ptr SQLCreateTableStatement::hashForeignKey()
{
  for (SQLTableElement_ptr element : *this->tableElementList)
  {
    if (instanceof <SQLTableElement, SQLForeignKeyConstraint>(element))
    {
      return BOOL::TRUE;
    }
  }
  return BOOL::FALSE;
}

BOOL_ptr SQLCreateTableStatement::isReferenced(SQLName_ptr tableName)
{
  if (tableName == NULL)
  {
    return BOOL::FALSE;
  }

  return isReferenced(tableName->getSimpleName());
}

BOOL_ptr SQLCreateTableStatement::isReferenced(string_ptr tableName)
{
  if (tableName->empty())
  {
    return BOOL::FALSE;
  }

  tableName = SQLUtils::normalize(tableName);

  for (SQLTableElement_ptr element : *this->tableElementList)
  {
    if (instanceof <SQLTableElement, SQLForeignKeyConstraint>(element))
    {
      SQLForeignKeyConstraint_ptr fk = std::dynamic_pointer_cast<SQLForeignKeyConstraint>(element);
      string_ptr refTableName = fk->getReferencedTableName()->getSimpleName();

      if (SQLUtils::nameEquals(tableName, refTableName))
      {
        return BOOL::TRUE;
      }
    }
  }

  return BOOL::FALSE;
}

// SQLAlterTableStatement* SQLCreateTableStatement::foreignKeyToAlterTable()
// {
//   SQLAlterTableStatement* stmt = new SQLAlterTableStatement();
//   std::list<SQLTableElement_ptr >::iterator it = this->tableElementList.begin();
//   for (int i = this->tableElementList.size() - 1; i >= 0; --i)
//   {
//     std::advance(it, i);
//     SQLTableElement_ptr element = (*it);
//     if (instanceof<SQLTableElement, SQLForeignKeyConstraint>(element)  )
//     {
//       SQLForeignKeyConstraint* fk = dynamic_cast<SQLForeignKeyConstraint*>(element);
//       this->tableElementList.erase(it);
//       stmt->addItem(new SQLAlterTableAddConstraint(fk));
//     }
//   }

//   if (stmt->getItems().empty())
//   {
//     return NULL;
//   }

//   stmt->setDbType(getDbType());
//   stmt->setTableSource(this->tableSource->clone());

//   Collections->reverse(stmt->getItems());

//   return stmt;
// }

void SQLCreateTableStatement::sort(SQLStatement_list_ptr stmtList)
{
  std::shared_ptr<std::map<string_ptr, SQLCreateTableStatement_ptr>> tables = std::make_shared<std::map<string_ptr, SQLCreateTableStatement_ptr>>();
  std::shared_ptr<std::map<string_ptr, SQLCreateTableStatement_list_ptr>> referencedTables = std::make_shared<std::map<string_ptr, SQLCreateTableStatement_list_ptr>>();

  for (SQLStatement_ptr stmt : *stmtList)
  {
    if (instanceof <SQLStatement, SQLCreateTableStatement>(stmt))
    {
      SQLCreateTableStatement_ptr createTableStmt = std::dynamic_pointer_cast<SQLCreateTableStatement>(stmt);
      string_ptr tableName = createTableStmt->getName()->getSimpleName();
      tableName = StringUtils::toLower(SQLUtils::normalize(tableName));
      tables->insert(std::make_pair(tableName, createTableStmt));
    }
  }

  std::shared_ptr<std::list<std::shared_ptr<ListDG::Edge>>> edges = std::make_shared<std::list<std::shared_ptr<ListDG::Edge>>>();

  for (auto stmt = tables->begin(); stmt != tables->end(); ++stmt)
  {
    for (SQLTableElement_ptr element : *stmt->second->getTableElementList())
    {
      if (instanceof <SQLTableElement, SQLForeignKeyConstraint>(element))
      {
        SQLForeignKeyConstraint_ptr fk = std::dynamic_pointer_cast<SQLForeignKeyConstraint>(element);
        string_ptr refTableName = fk->getReferencedTableName()->getSimpleName();
        refTableName = StringUtils::toLower(SQLUtils::normalize(refTableName));

        auto it = tables->find(refTableName);
        SQLCreateTableStatement_ptr refTable;
        if (it != tables->end())
        {
          refTable = it->second;
        }
        if (refTable != NULL)
        {
          edges->push_back(std::shared_ptr<ListDG::Edge>(new ListDG::Edge(stmt->second, refTable)));
        }

        auto referencedTable = referencedTables->find(refTableName);
        SQLCreateTableStatement_list_ptr referencedList;
        if (referencedTable != referencedTables->end())
        {
          referencedList = referencedTable->second;
        }
        if (referencedList->empty())
        {
          // referencedList = new ArrayList<SQLCreateTableStatement>();
          referencedTables->insert(std::make_pair(refTableName, referencedList));
        }
        referencedList->push_back(stmt->second);
      }
    }
  }

  for (SQLStatement_ptr stmt : *stmtList)
  {
    // if (instanceof<SQLStatement, OracleCreateSynonymStatement>(stmt)  )
    // {
    //   OracleCreateSynonymStatement* createSynonym = dynamic_cast<OracleCreateSynonymStatement*>(stmt);
    //   SQLName_ptr object = createSynonym->getObject();
    //   string_ptr  refTableName = object->getSimpleName();
    //   SQLCreateTableStatement* refTable = tables->get(refTableName);
    //   if (refTable != NULL)
    //   {
    //     edges.push_back(new ListDG::Edge(dynamic_cast<Object_ptr >(stmt), dynamic_cast<Object_ptr >(refTable)));
    //   }
    // }
  }

  ListDG *dg = new ListDG(ListTran::tran_class<SQLStatement, Object>(stmtList), edges);

  // SQLStatement_ptr  tops[] = new SQLStatement[stmtList.size()];
  SQLStatement_list_ptr tops = SQLStatement_list_ptr();
  if (dg->topologicalSort(ListTran::tran_class<SQLStatement, Object>(tops)))
  {
    auto it = stmtList->begin();
    auto it_tops = tops->begin();

    for (int i = 0, size = stmtList->size(); i < size; ++i)
    {
      std::advance(it, i);
      std::advance(it_tops, i - 1);
      stmtList->insert(it, *it_tops);
    }
    return;
  }

  // std::list<SQLAlterTableStatement*> alterList ;

  auto it_edges = edges->begin();
  for (int i = edges->size() - 1; i >= 0; --i)
  {
    std::advance(it_edges, i);
    std::shared_ptr<ListDG::Edge> edge = (*it_edges);
    SQLCreateTableStatement *from = std::dynamic_pointer_cast<SQLCreateTableStatement>(edge->from).get();
    string_ptr fromTableName = from->getName()->getSimpleName();
    fromTableName = StringUtils::toLower(SQLUtils::normalize(fromTableName));
    auto it_referencedTables = referencedTables->find(fromTableName);
    if (it_referencedTables != referencedTables->end())
    {
      edges->erase(it_edges);

      // Arrays->fill(tops, NULL);
      // tops = new SQLStatement[stmtList.size()];

      dg = new ListDG(ListTran::tran_class<SQLStatement, Object>(stmtList), edges);
      if (dg->topologicalSort(ListTran::tran_class<SQLStatement, Object>(tops)))
      {
        auto it_stmtList = stmtList->begin();
        auto it_tops = tops->begin();
        for (int j = 0, size = stmtList->size(); j < size; ++j)
        {
          std::advance(it_tops, size - j - 1);
          SQLStatement_ptr stmt = *it_tops;
          std::advance(it_stmtList, j);
          (*it_stmtList) = stmt;
        }

        // SQLAlterTableStatement* alter = from->foreignKeyToAlterTable();
        // alterList.push_back(alter);

        // stmtList.push_back(alter);
        return;
      }
      edges->insert(it_edges, edge);
    }
  }

  for (int i = edges->size() - 1; i >= 0; --i)
  {
    std::advance(it_edges, i);
    std::shared_ptr<ListDG::Edge> edge = (*it_edges);
    SQLCreateTableStatement *from = std::dynamic_pointer_cast<SQLCreateTableStatement>(edge->from).get();
    string_ptr fromTableName = from->getName()->getSimpleName();
    fromTableName = StringUtils::toLower(SQLUtils::normalize(fromTableName));
    auto it_referencedTable = referencedTables->find(fromTableName);
    if (it_referencedTable != referencedTables->end())
    {
      // SQLAlterTableStatement* alter = from->foreignKeyToAlterTable();

      edges->erase(it_edges);
      // if (alter != NULL)
      // {
      //   alterList->add(alter);
      // }

      // Arrays->fill(tops, NULL);
      // tops = new SQLStatement[stmtList.size()];

      dg = new ListDG(ListTran::tran_class<SQLStatement, Object>(stmtList), edges);
      if (dg->topologicalSort(ListTran::tran_class<SQLStatement, Object>(tops)))
      {
        auto it_stmtList = stmtList->begin();
        auto it_tops = tops->begin();
        for (int j = 0, size = stmtList->size(); j < size; ++j)
        {
          std::advance(it_tops, size - j - 1);
          SQLStatement_ptr stmt = *it_tops;
          std::advance(it_stmtList, j);
          // stmtList->set(j, stmt);
          (*it_stmtList) = stmt;
        }

        // stmtList->addAll(alterList);
        return;
      }
    }
  }
}

void SQLCreateTableStatement::simplify()
{
  SQLName_ptr name = getName();
  if (instanceof <SQLName, SQLPropertyExpr>(name))
  {
    string_ptr tableName = (std::dynamic_pointer_cast<SQLPropertyExpr>(name))->getName();
    tableName = SQLUtils::normalize(tableName);

    string_ptr normalized = SQLUtils::normalize(tableName, dbType);
    if (*tableName != *normalized)
    {
      this->setName(normalized);
      name = getName();
    }
  }

  if (instanceof <SQLName, SQLIdentifierExpr>(name))
  {
    SQLIdentifierExpr_ptr identExpr = std::dynamic_pointer_cast<SQLIdentifierExpr>(name);
    string_ptr tableName = identExpr->getName();
    string_ptr normalized = SQLUtils::normalize(tableName, dbType);
    if (*normalized != *tableName)
    {
      setName(normalized);
    }
  }

  for (SQLTableElement_ptr element : *this->tableElementList)
  {
    if (instanceof <SQLTableElement, SQLColumnDefinition>(element))
    {
      SQLColumnDefinition_ptr column = std::dynamic_pointer_cast<SQLColumnDefinition>(element);
      column->simplify();
    }
    else if (instanceof <SQLTableElement, SQLConstraint>(element))
    {
      (std::dynamic_pointer_cast<SQLConstraint>(element))->simplify();
    }
  }
}

BOOL_ptr SQLCreateTableStatement::apply(SQLDropIndexStatement_ptr x)
{
  long indexNameHashCode64 = x->getIndexName()->nameHashCode64();

  auto it_tableElementList = tableElementList->begin();
  for (int i = tableElementList->size() - 1; i >= 0; i--)
  {
    std::advance(it_tableElementList, i);
    SQLTableElement_ptr e = *it_tableElementList;
    if (instanceof <SQLTableElement, SQLUniqueConstraint>(e))
    {
      SQLUniqueConstraint_ptr unique = std::dynamic_pointer_cast<SQLUniqueConstraint>(e);
      if (unique->getName() != NULL && unique->getName()->nameHashCode64() == indexNameHashCode64)
      {
        if (it_tableElementList != tableElementList->end())
          tableElementList->erase(it_tableElementList);
        // tableElementList->remove(i);
        return BOOL::TRUE;
      }
    }
    else if (instanceof <SQLTableElement, MySqlTableIndex>(e))
    {
      MySqlTableIndex_ptr tableIndex = std::dynamic_pointer_cast<MySqlTableIndex>(e);
      if (SQLUtils::nameEquals(tableIndex->getName(), x->getIndexName()))
      {
        // tableElementList->remove(i);
        if (it_tableElementList != tableElementList->end())
          tableElementList->erase(it_tableElementList);
        return BOOL::TRUE;
      }
    }
  }
  return BOOL::FALSE;
}

// BOOL_ptr SQLCreateTableStatement::apply(SQLCommentStatement x)
// {
//   SQLName_ptr on = x->getOn()->getName();
//   SQLExpr_ptr comment = x->getComment();
//   if (comment == NULL)
//   {
//     return BOOL::FALSE;
//   }

//   SQLCommentStatement->Type type = x->getType();
//   if (type == SQLCommentStatement->Type->TABLE)
//   {
//     if (!SQLUtils::nameEquals(getName(), on))
//     {
//       return BOOL::FALSE;
//     }

//     setComment(comment->clone());

//     return BOOL::TRUE;
//   }
//   else if (type == SQLCommentStatement->Type->COLUMN)
//   {
//     SQLPropertyExpr propertyExpr = (SQLPropertyExpr)on;
//     if (!SQLUtils::nameEquals(getName(), (SQLName_ptr )propertyExpr->getOwner()))
//     {
//       return BOOL::FALSE;
//     }

//     SQLColumnDefinition *column = this->findColumn(
//         propertyExpr->nameHashCode64());

//     if (column != NULL)
//     {
//       column->setComment(comment->clone());
//     }
//     return BOOL::TRUE;
//   }

//   return BOOL::FALSE;
// }

// BOOL_ptr SQLCreateTableStatement::apply(SQLAlterTableStatement alter)
// {
//   if (!SQLUtils::nameEquals(alter->getName(), this->getName()))
//   {
//     return BOOL::FALSE;
//   }

//   int applyCount = 0;
//   for (SQLAlterTableItem item : alter->getItems())
//   {
//     if (alterApply(item))
//     {
//       applyCount++;
//     }
//   }

//   return applyCount > 0;
// }

BOOL_ptr SQLCreateTableStatement::alterApply(SQLAlterTableItem *item)
{
  // if (item instanceof SQLAlterTableDropColumnItem)
  // {
  //   return apply((SQLAlterTableDropColumnItem)item);
  // }
  // else if (item instanceof SQLAlterTableAddColumn)
  // {
  //   return apply((SQLAlterTableAddColumn)item);
  // }
  // else if (item instanceof SQLAlterTableAddConstraint)
  // {
  //   return apply((SQLAlterTableAddConstraint)item);
  // }
  // else if (item instanceof SQLAlterTableDropPrimaryKey)
  // {
  //   return apply((SQLAlterTableDropPrimaryKey)item);
  // }
  // else if (item instanceof SQLAlterTableDropIndex)
  // {
  //   return apply((SQLAlterTableDropIndex)item);
  // }
  // else if (item instanceof SQLAlterTableDropConstraint)
  // {
  //   return apply((SQLAlterTableDropConstraint)item);
  // }
  // else if (item instanceof SQLAlterTableDropKey)
  // {
  //   return apply((SQLAlterTableDropKey)item);
  // }
  // else if (item instanceof SQLAlterTableDropForeignKey)
  // {
  //   return apply((SQLAlterTableDropForeignKey)item);
  // }
  // else if (item instanceof SQLAlterTableRename)
  // {
  //   return apply((SQLAlterTableRename)item);
  // }
  // else if (item instanceof SQLAlterTableRenameColumn)
  // {
  //   return apply((SQLAlterTableRenameColumn)item);
  // }
  // else if (item instanceof SQLAlterTableAddIndex)
  // {
  //   return apply((SQLAlterTableAddIndex)item);
  // }

  return BOOL::FALSE;
}

// // SQLAlterTableRenameColumn

// BOOL_ptr SQLCreateTableStatement::apply(SQLAlterTableRenameColumn *item)
// {
//   int columnIndex = columnIndexOf(item->getColumn());
//   if (columnIndex == -1)
//   {
//     return BOOL::FALSE;
//   }

//   SQLColumnDefinition *column = (SQLColumnDefinition *)tableElementList->get(columnIndex);
//   column->setName(item->getTo()->clone());

//   return BOOL::TRUE;
// }

BOOL_ptr SQLCreateTableStatement::renameColumn(string_ptr colummName, string_ptr newColumnName)
{
  if (colummName->empty() || newColumnName->empty() || newColumnName->length() == 0)
  {
    return BOOL::FALSE;
  }

  int columnIndex = columnIndexOf(SQLIdentifierExpr_ptr(new SQLIdentifierExpr((colummName))));
  if (columnIndex == -1)
  {
    return BOOL::FALSE;
  }

  auto it_tableElementList = tableElementList->begin();
  std::advance(it_tableElementList, columnIndex);
  SQLColumnDefinition_ptr column = std::dynamic_pointer_cast<SQLColumnDefinition>(*it_tableElementList);
  column->setName(SQLIdentifierExpr_ptr(new SQLIdentifierExpr((newColumnName))));

  return BOOL::TRUE;
}

// BOOL_ptr SQLCreateTableStatement::apply(SQLAlterTableRename *item)
// {
//   SQLName_ptr name = item->getToName();
//   if (name == NULL)
//   {
//     return BOOL::FALSE;
//   }

//   this->setName(name->clone());

//   return BOOL::TRUE;
// }

// BOOL_ptr SQLCreateTableStatement::apply(SQLAlterTableDropForeignKey *item)
// {
//   for (int i = tableElementList->size() - 1; i >= 0; i--)
//   {
//     SQLTableElement_ptr e = tableElementList->get(i);
//     if (e instanceof SQLForeignKeyConstraint)
//     {
//       SQLForeignKeyConstraint fk = (SQLForeignKeyConstraint)e;
//       if (SQLUtils::nameEquals(fk->getName(), item->getIndexName()))
//       {
//         tableElementList->remove(i);
//         return BOOL::TRUE;
//       }
//     }
//   }
//   return BOOL::FALSE;
// }

// BOOL_ptr SQLCreateTableStatement::apply(SQLAlterTableDropKey *item)
// {
//   for (int i = tableElementList->size() - 1; i >= 0; i--)
//   {
//     SQLTableElement_ptr e = tableElementList->get(i);
//     if (e instanceof SQLUniqueConstraint)
//     {
//       SQLUniqueConstraint unique = (SQLUniqueConstraint)e;
//       if (SQLUtils::nameEquals(unique->getName(), item->getKeyName()))
//       {
//         tableElementList->remove(i);
//         return BOOL::TRUE;
//       }
//     }
//   }
//   return BOOL::FALSE;
// }

// BOOL_ptr SQLCreateTableStatement::apply(SQLAlterTableDropConstraint *item)
// {
//   for (int i = tableElementList->size() - 1; i >= 0; i--)
//   {
//     SQLTableElement_ptr e = tableElementList->get(i);
//     if (e instanceof SQLConstraint)
//     {
//       SQLConstraint constraint = (SQLConstraint)e;
//       if (SQLUtils::nameEquals(constraint->getName(), item->getConstraintName()))
//       {
//         tableElementList->remove(i);
//         return BOOL::TRUE;
//       }
//     }
//   }
//   return BOOL::FALSE;
// }

// BOOL_ptr SQLCreateTableStatement::apply(SQLAlterTableDropIndex *item)
// {
//   for (int i = tableElementList->size() - 1; i >= 0; i--)
//   {
//     SQLTableElement_ptr e = tableElementList->get(i);
//     if (e instanceof SQLUniqueConstraint)
//     {
//       SQLUniqueConstraint unique = (SQLUniqueConstraint)e;
//       if (SQLUtils::nameEquals(unique->getName(), item->getIndexName()))
//       {
//         tableElementList->remove(i);
//         return BOOL::TRUE;
//       }
//     }
//     else if (e instanceof MySqlTableIndex)
//     {
//       MySqlTableIndex tableIndex = (MySqlTableIndex)e;
//       if (SQLUtils::nameEquals(tableIndex->getName(), item->getIndexName()))
//       {
//         tableElementList->remove(i);
//         return BOOL::TRUE;
//       }
//     }
//   }
//   return BOOL::FALSE;
// }

// BOOL_ptr SQLCreateTableStatement::apply(SQLAlterTableDropPrimaryKey *item)
// {
//   for (int i = tableElementList->size() - 1; i >= 0; i--)
//   {
//     SQLTableElement_ptr e = tableElementList->get(i);
//     if (e instanceof SQLPrimaryKey *)
//     {
//       tableElementList->remove(i);
//       return BOOL::TRUE;
//     }
//   }
//   return BOOL::FALSE;
// }

// BOOL_ptr SQLCreateTableStatement::apply(SQLAlterTableAddConstraint *item)
// {
//   SQLName_ptr name = item->getConstraint()->getName();
//   if (name != NULL)
//   {
//     long nameHashCode = name->nameHashCode64();
//     for (int i = tableElementList->size() - 1; i >= 0; i--)
//     {
//       SQLTableElement_ptr e = tableElementList->get(i);
//       if (e instanceof SQLConstraint)
//       {
//         SQLName_ptr name1 = ((SQLConstraint)e)->getName();
//         if (name1 != NULL && name1->nameHashCode64() == nameHashCode)
//         {
//           return BOOL::FALSE;
//         }
//       }
//     }
//   }
//   tableElementList->add((SQLTableElement_ptr )item->getConstraint());
//   return BOOL::TRUE;
// }

// BOOL_ptr SQLCreateTableStatement::apply(SQLAlterTableDropColumnItem *item)
// {
//   // for (SQLName* column : item->getColumns()) {
//   //     string_ptr  columnName = column->getSimpleName();
//   //     for (int i = tableElementList->size() - 1; i >= 0; --i) {
//   //         SQLTableElement* e = tableElementList->get(i);
//   //         if (e instanceof SQLColumnDefinition*) {
//   //             if (SQLUtils::nameEquals(columnName, ((SQLColumnDefinition*) e)->getName()->getSimpleName())) {
//   //                 tableElementList->remove(i);
//   //             }
//   //         }
//   //     }

//   //     for (int i = tableElementList->size() - 1; i >= 0; --i) {
//   //         SQLTableElement* e = tableElementList->get(i);
//   //         if (e instanceof SQLUnique) {
//   //             SQLUnique unique = (SQLUnique) e;
//   //             unique->applyDropColumn(column);
//   //             if (unique->getColumns()->isEmpty()) {
//   //                 tableElementList->remove(i);
//   //             }
//   //         } else if (e instanceof MySqlTableIndex) {
//   //             MySqlTableIndex* index = (MySqlTableIndex*) e;
//   //             index->applyDropColumn(column);
//   //             if (index->getColumns()->isEmpty()) {
//   //                 tableElementList->remove(i);
//   //             }
//   //         }
//   //     }
//   // }

//   return BOOL::TRUE;
// }

// BOOL_ptr SQLCreateTableStatement::apply(SQLAlterTableAddIndex *item)
// {
//   return BOOL::FALSE;
// }

// BOOL_ptr SQLCreateTableStatement::apply(SQLAlterTableAddColumn *item)
// {
//   int startIndex = tableElementList->size();
//   if (item->isFirst())
//   {
//     startIndex = 0;
//   }

//   int afterIndex = columnIndexOf(item->getAfterColumn());
//   if (afterIndex != -1)
//   {
//     startIndex = afterIndex + 1;
//   }

//   int beforeIndex = columnIndexOf(item->getFirstColumn());
//   if (beforeIndex != -1)
//   {
//     startIndex = beforeIndex;
//   }

//   // for (int i = 0; i < item->getColumns()->size(); i++) {
//   //     SQLColumnDefinition* column = item->getColumns()->get(i);

//   //     int matchIndex = -1;
//   //     for (int j = 0; j < tableElementList->size(); j++) {
//   //         SQLTableElement* element = tableElementList->get(j);

//   //         if (element instanceof SQLColumnDefinition*) {
//   //             if (column->nameHashCode64() == (((SQLColumnDefinition*) element))->nameHashCode64()) {
//   //                 matchIndex = j;
//   //                 break;
//   //             }
//   //         }
//   //     }

//   //     if (matchIndex != -1) {
//   //         return BOOL::FALSE;
//   //     }

//   //     tableElementList->add(i + startIndex, column);

//   //     column->setParent(this);
//   // }

//   return BOOL::TRUE;
// }

int SQLCreateTableStatement::columnIndexOf(SQLName_ptr column)
{
  if (column == NULL)
  {
    return -1;
  }

  string_ptr columnName = column->getSimpleName();
  auto it_tableElementList = tableElementList->begin();
  for (int i = tableElementList->size() - 1; i >= 0; --i)
  {
    std::advance(it_tableElementList, i);
    SQLTableElement_ptr e = (*it_tableElementList);
    if (instanceof <SQLTableElement, SQLColumnDefinition>(e))
    {
      if (SQLUtils::nameEquals(columnName, (std::dynamic_pointer_cast<SQLColumnDefinition>(e))->getName()->getSimpleName()))
      {
        return i;
      }
    }
  }

  return -1;
}

void SQLCreateTableStatement::cloneTo(SQLCreateTableStatement_ptr x)
{
  x->setExternal(external);
  x->ifNotExists = ifNotExists;
  x->type = type;

  if (tableSource != NULL)
  {
    x->setTableSource(std::dynamic_pointer_cast<SQLExprTableSource>(tableSource->clone()));
  }

  for (SQLTableElement_ptr e : *tableElementList)
  {
    SQLTableElement_ptr e2 = std::dynamic_pointer_cast<SQLTableElement>(e->clone());
    e2->setParent(x);
    x->tableElementList->push_back(e2);
  }

  for (SQLColumnDefinition_ptr e : *partitionColumns)
  {
    SQLColumnDefinition_ptr e2 = std::dynamic_pointer_cast<SQLColumnDefinition>(e->clone());
    e2->setParent(x);
    x->partitionColumns->push_back(e2);
  }

  if (inherits != NULL)
  {
    x->setInherits(std::dynamic_pointer_cast<SQLExprTableSource>(inherits->clone()));
  }

  if (select != NULL)
  {
    x->setSelect(std::dynamic_pointer_cast<SQLSelect>(select->clone()));
  }

  if (comment != NULL)
  {
    x->setComment(std::dynamic_pointer_cast<SQLExpr>(comment->clone()));
  }

  // if (partitioning != NULL)
  // {
  //   x->setPartitioning(partitioning->clone());
  // }

  if (like != NULL)
  {
    x->setLike(std::dynamic_pointer_cast<SQLExprTableSource>(like->clone()));
  }

  x->compress = compress;
  x->logging = logging;

  if (tablespace != NULL)
  {
    x->setTablespace(std::dynamic_pointer_cast<SQLName>(tablespace->clone()));
  }

  // if (partitioning != NULL)
  // {
  //   x->setPartitioning(partitioning->clone());
  // }

  // if (localPartitioning != NULL)
  // {
  //   x->setLocalPartitioning(localPartitioning->clone());
  // }

  if (storedAs != NULL)
  {
    x->setStoredAs(std::dynamic_pointer_cast<SQLExpr>(storedAs->clone()));
  }

  if (location != NULL)
  {
    x->setLocation(std::dynamic_pointer_cast<SQLExpr>(location->clone()));
  }

  x->onCommitPreserveRows = onCommitPreserveRows;
  x->onCommitDeleteRows = onCommitDeleteRows;

  for (SQLAssignItem_ptr item : *this->tableOptions)
  {
    SQLAssignItem_ptr item2 = std::dynamic_pointer_cast<SQLAssignItem>(item->clone());
    item2->setParent(item);
    x->tableOptions->push_back(item2);
  }

  for (SQLAssignItem_ptr item : *this->tblProperties)
  {
    SQLAssignItem_ptr item2 = std::dynamic_pointer_cast<SQLAssignItem>(item->clone());
    item2->setParent(item);
    x->tblProperties->push_back(item2);
  }

  // if (rowFormat != NULL)
  // {
  //   x->setRowFormat(rowFormat->clone());
  // }

  // if (clusteringType != NULL)
  // {
  //   x->setClusteringType(clusteringType);
  // }

  for (SQLSelectOrderByItem_ptr e : *clusteredBy)
  {
    SQLSelectOrderByItem_ptr e2 = std::dynamic_pointer_cast<SQLSelectOrderByItem>(e->clone());
    e2->setParent(x);
    x->clusteredBy->push_back(e2);
  }

  for (SQLSelectOrderByItem_ptr e : *sortedBy)
  {
    SQLSelectOrderByItem_ptr e2 = std::dynamic_pointer_cast<SQLSelectOrderByItem>(e->clone());
    e2->setParent(x);
    x->sortedBy->push_back(e2);
  }

  x->buckets = buckets;
  x->shards = shards;
  x->dimension = dimension;
}

BOOL_ptr SQLCreateTableStatement::isReplace()
{
  return replace;
}

void SQLCreateTableStatement::setReplace(BOOL_ptr replace)
{
  this->ignore = BOOL::FALSE;
  this->replace = replace;
}

BOOL_ptr SQLCreateTableStatement::isIgnore()
{
  return ignore;
}

void SQLCreateTableStatement::setIgnore(BOOL_ptr ignore)
{
  this->replace = BOOL::FALSE;
  this->ignore = ignore;
}

BOOL_ptr SQLCreateTableStatement::isSingle()
{
  return single;
}

void SQLCreateTableStatement::setSingle(BOOL_ptr single)
{
  this->single = single;
}

SQLExpr_ptr SQLCreateTableStatement::getStoredAs()
{
  return storedAs;
}

void SQLCreateTableStatement::setStoredAs(SQLExpr_ptr x)
{
  if (x != NULL)
  {
    x->setParent(SharedObject(SQLCreateTableStatement));
  }
  this->storedAs = x;
}

SQLObject_ptr SQLCreateTableStatement::clone()
{
  SQLObject_ptr clone_tmp = SQLCreateTableStatement_ptr(new SQLCreateTableStatement(dbType));
  SQLCreateTableStatement_ptr x = std::dynamic_pointer_cast<SQLCreateTableStatement>(clone_tmp);
  // SQLCreateTableStatement *x = new SQLCreateTableStatement(&dbType);
  cloneTo(x);
  return x;
}

string_ptr SQLCreateTableStatement::toString()
{
  string_ptr tmp = SQLUtils::toSQLString(static_cast<Object_ptr >(this), dbType);
  return (tmp);
}

BOOL_ptr SQLCreateTableStatement::isOnCommitPreserveRows()
{
  return onCommitPreserveRows;
}

void SQLCreateTableStatement::setOnCommitPreserveRows(BOOL_ptr onCommitPreserveRows)
{
  this->onCommitPreserveRows = onCommitPreserveRows;
}

// for odps & hive
BOOL_ptr SQLCreateTableStatement::isExternal()
{
  return external;
}

void SQLCreateTableStatement::setExternal(BOOL_ptr external)
{
  this->external = external;
}

// ClusteringType *SQLCreateTableStatement::getClusteringType()
// {
//   return clusteringType;
// }

// void SQLCreateTableStatement::setClusteringType(ClusteringType *clusteringType)
// {
//   this->clusteringType = clusteringType;
// }

SQLSelectOrderByItem_list_ptr SQLCreateTableStatement::getClusteredBy()
{
  return clusteredBy;
}

void SQLCreateTableStatement::addClusteredByItem(SQLSelectOrderByItem_ptr item)
{
  item->setParent(SharedObject(SQLCreateTableStatement));
  this->clusteredBy->push_back(item);
}

SQLSelectOrderByItem_list_ptr SQLCreateTableStatement::getSortedBy()
{
  return sortedBy;
}

void SQLCreateTableStatement::addSortedByItem(SQLSelectOrderByItem_ptr item)
{
  item->setParent(SharedObject(SQLCreateTableStatement));
  this->sortedBy->push_back(item);
}

int SQLCreateTableStatement::getBuckets()
{
  return buckets;
}

void SQLCreateTableStatement::setBuckets(int buckets)
{
  this->buckets = buckets;
}

int SQLCreateTableStatement::getShards()
{
  return shards;
}

void SQLCreateTableStatement::setShards(int shards)
{
  this->shards = shards;
}

SQLColumnDefinition_list_ptr SQLCreateTableStatement::getPartitionColumns()
{
  return partitionColumns;
}

void SQLCreateTableStatement::addPartitionColumn(SQLColumnDefinition_ptr column)
{
  if (column != NULL)
  {
    column->setParent(SharedObject(SQLCreateTableStatement));
  }
  this->partitionColumns->push_back(column);
}

SQLAssignItem_list_ptr SQLCreateTableStatement::getTableOptions()
{
  return tableOptions;
}

SQLAssignItem_list_ptr SQLCreateTableStatement::getTblProperties()
{
  return tblProperties;
}

void SQLCreateTableStatement::addTblProperty(string_ptr name, SQLExpr_ptr value)
{
  SQLAssignItem_ptr assignItem =
      SQLAssignItem_ptr(new SQLAssignItem(SQLIdentifierExpr_ptr(new SQLIdentifierExpr((name))), value));
  assignItem->setParent(SharedObject(SQLCreateTableStatement));
  tblProperties->push_back(assignItem);
}

SQLExternalRecordFormat_ptr SQLCreateTableStatement::getRowFormat()
{
  return rowFormat;
}

void SQLCreateTableStatement::setRowFormat(SQLExternalRecordFormat_ptr x)
{
  if (x != nullptr)
  {
    x->setParent(SharedObject(SQLCreateTableStatement));
  }
  this->rowFormat = x;
}

BOOL_ptr SQLCreateTableStatement::isDimension()
{
  return dimension;
}

void SQLCreateTableStatement::setDimension(BOOL_ptr dimension)
{
  this->dimension = dimension;
}

SQLExpr_ptr SQLCreateTableStatement::getLocation()
{
  return location;
}

void SQLCreateTableStatement::setLocation(SQLExpr_ptr x)
{
  if (x != NULL)
  {
    x->setParent(SharedObject(SQLCreateTableStatement));
  }
  this->location = x;
}

void SQLCreateTableStatement::addOption(string_ptr name, SQLExpr_ptr value)
{
  SQLAssignItem_ptr assignItem =
      SQLAssignItem_ptr(new SQLAssignItem(SQLIdentifierExpr_ptr(new SQLIdentifierExpr((name))), value));
  assignItem->setParent(SharedObject(SQLCreateTableStatement));
  tableOptions->push_back(assignItem);
}

SQLExpr_ptr SQLCreateTableStatement::getOption(string_ptr name)
{
  if (name->empty())
  {
    return NULL;
  }

  long hash64 = FnvHash::hashCode64(name);

  for (SQLAssignItem_ptr item : *tableOptions)
  {
    SQLExpr_ptr target = item->getTarget();
    if (instanceof <SQLExpr, SQLIdentifierExpr>(target))
    {
      if ((std::dynamic_pointer_cast<SQLIdentifierExpr>(target))->hashCode64() == hash64)
      {
        return item->getValue();
      }
    }
  }

  return NULL;
}

SQLExpr_ptr SQLCreateTableStatement::getTblProperty(string_ptr name)
{
  if (name->empty())
  {
    return NULL;
  }

  long hash64 = FnvHash::hashCode64(name);

  for (SQLAssignItem_ptr item : *tblProperties)
  {
    SQLExpr_ptr target = item->getTarget();
    if (instanceof <SQLExpr, SQLIdentifierExpr>(target))
    {
      if ((std::dynamic_pointer_cast<SQLIdentifierExpr>(target))->hashCode64() == hash64)
      {
        return item->getValue();
      }
    }
  }

  return NULL;
}

Object_ptr SQLCreateTableStatement::getOptionValue(string_ptr name)
{
  SQLExpr_ptr option = getOption(name);
  if (instanceof <SQLExpr, SQLValuableExpr>(option.get()))
  {
    return std::dynamic_pointer_cast<SQLValuableExpr>(option)->getValue();
  }

  return NULL;
}

Object_ptr SQLCreateTableStatement::getTblPropertyValue(string_ptr name)
{
  SQLExpr_ptr option = getTblProperty(name);
  if (instanceof <SQLExpr, SQLValuableExpr>(option.get()))
  {
    return std::dynamic_pointer_cast<SQLValuableExpr>(option)->getValue();
  }

  return NULL;
}

Object_ptr SQLCreateTableStatement::getOptionOrTblPropertyValue(string_ptr name)
{
  SQLExpr_ptr option = getTblProperty(name);

  if (option == NULL)
  {
    option = getOption(name);
  }
  if (instanceof <SQLExpr, SQLValuableExpr>(option.get()))
  {
    return std::dynamic_pointer_cast<SQLValuableExpr>(option)->getValue();
  }
  return NULL;
}

string_ptr SQLCreateTableStatement::getCatalog()
{
  return NULL;
}

BOOL_ptr SQLCreateTableStatement::containsDuplicateColumnNames()
{
  return containsDuplicateColumnNames(BOOL::FALSE);
}

BOOL_ptr SQLCreateTableStatement::containsDuplicateColumnNames(BOOL_ptr throwException)
{
  std::shared_ptr<std::map<long, SQLTableElement_ptr>> columnMap = std::shared_ptr<std::map<long, SQLTableElement_ptr>>();

  for (SQLTableElement_ptr item : *tableElementList)
  {
    if (instanceof <SQLTableElement, SQLColumnDefinition>(item))
    {
      SQLName_ptr columnName = (std::dynamic_pointer_cast<SQLColumnDefinition>(item))->getName();
      long nameHashCode64 = columnName->nameHashCode64();
      columnMap->insert(std::pair<long, SQLTableElement_ptr>(nameHashCode64, item));
      // SQLTableElement_ptr old;
      // if (old != NULL)
      // {
      //   if (throwException)
      //   {
      //     throw new SemanticException("Table contains duplicate column names : " + SQLUtils::normalize(columnName->getSimpleName()));
      //   }
      //   return BOOL::TRUE;
      // }
    }
  }

  return BOOL::FALSE;
}

SQLExpr_ptr SQLCreateTableStatement::getEngine()
{
  return engine;
}

void SQLCreateTableStatement::setEngine(SQLExpr_ptr x)
{
  if (x != NULL)
  {
    x->setParent(SharedObject(SQLCreateTableStatement));
  }
  this->engine = x;
}

// DDLObjectType *SQLCreateTableStatement::getDDLObjectType()
// {
//   return DDLObjectType *->TABLE;
// }
